<?php

namespace Payment\Common\SwThree;

use Payment\Common\BaseData;
use Payment\Common\BaseStrategy;
use Payment\Common\PayException;
use Payment\Common\SwThreeConfig;
use Payment\Utils\ArrayUtil;
use Payment\Utils\Curl;


abstract class SwBaseStrategy implements BaseStrategy
{


    protected $config;

    /**
     * 支付数据
     * @var BaseData $reqData
     */
    protected $reqData;

    /**
     * SwBaseStrategy constructor.
     * @param array $config
     * @throws PayException
     */
    public function __construct(array $config)
    {
        /* 设置内部字符编码为 UTF-8 */
        mb_internal_encoding("UTF-8");

        try {
            $this->config = new SwThreeConfig($config);
        } catch (PayException $e) {
            throw $e;
        }
    }

    /**
     * 发送完了请求
     * @param array $body
     * @return mixed
     * @throws PayException
     *
     */
    protected function sendReq($body)
    {
        $url = $this->getReqUrl();
        if (is_null($url)) {
            throw new PayException('目前不支持该接口。请联系开发者添加');
        }
        $responseTxt = $this->curlPost($body, $url);
        if ($responseTxt['error']) {
            throw new PayException('网络发生错误，请稍后再试curl返回码：' . $responseTxt['message']);
        }
        // 格式化为数组
        $retData = (json_decode($responseTxt['body'],true));
        if ($retData['return_code'] != '01') {
            throw new PayException('支付平台返回错误提示:' . $retData['return_msg']);
        }
        if ($retData['result_code'] == '02') {
            $msg = $retData['result_code'] .' : '. $retData['return_msg'];
            throw new PayException('支付平台返回错误提示:' . $msg);
        }

        return $retData;
    }


    protected function curlPost($body, $url)
    {
        $curl = new Curl();
        $this_header = array("content-type: application/json;charset=UTF-8");
        return $curl->set([
            'CURLOPT_HEADER' => 0,
            'CURLOPT_HTTPHEADER' => $this_header
        ])->post($body, $url)->submit($url,true);
    }

    /**
     * 获取需要的url  默认返回下单的url,根据实际情况，需要被覆写
     * 默认是h5支付
     *
     * @param null $url
     * @return null|string
     */
    protected function getReqUrl($url=null)
    {
        if(isset($this->config->base_url) && !empty($this->config->base_url)){
            $_pre = $this->config->base_url;
        }else{
            $_pre = SwThreeConfig::BASE_URL;
        }

        return $_pre.($url??SwThreeConfig::UNIFIED_URL);
    }

    /**
     * @param array $data
     *
     * @throws PayException
     * @return array|string
     */
    public function handle(array $data)
    {
        $buildClass = $this->getBuildDataClass();
        try {
            $this->reqData = new $buildClass($this->config, $data);
        } catch (PayException $e) {
            throw $e;
        }
        $this->reqData->setSign();
        $body = $this->reqData->getData();
        $ret = $this->sendReq($body);

        // 检查返回的数据是否被篡改
        $flag = true;// $this->verifySign($ret);
        if (!$flag) {
            throw new PayException('返回数据被篡改。请检查网络是否安全！');
        }

        return $this->retData($ret);
    }

    /**
     * 处理微信的返回值并返回给客户端
     * @param array $ret
     * @return mixed
     *
     */
    protected function retData(array $ret)
    {
        return $ret;
    }

    /**
     * 检查微信返回的数据是否被篡改过
     * @param array $retData
     * @return boolean
     *
     */
    protected function verifySign(array $retData)
    {
        $retSign = $retData['sign'];
        $values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']);

        $values = ArrayUtil::paraFilter($values);

        $values = ArrayUtil::arraySort($values);

        $signStr = ArrayUtil::createLinkstring($values);

        $signStr .= '&key=' . $this->config->md5Key;
        switch ($this->config->signType) {
            case 'MD5':
                $sign = md5($signStr);
                break;
            case 'HMAC-SHA256':
                $sign = hash_hmac('sha256', $signStr, $this->config->md5Key);
                break;
            default:
                $sign = '';
        }

        return strtoupper($sign) === $retSign;
    }
}
